#ifndef APOLLO_CONFIG_DETAIL_COMPILER_TRAITS_H_
#define APOLLO_CONFIG_DETAIL_COMPILER_TRAITS_H_
#include <apollo/config/detail/plateform.h>
#include <apollo/config/detail/compiler.h>
/** 
 * APOLLO_PREPROCESSOR_JOIN(book_, 2)  book_2
 * APOLLO_STRINGIF(x) "x"
 * APOLLO_COMPILER_INTMAX_SIZE int
 * APOLLO_OFFSETOF   size_t
 * APOLLO_SIZEOF_MEMBER size_t 
 * APOLLO_LIKELY
 * APOLLO_UNLIKELY
 * APOLLO_INIT_PROPERITY(x) for gcc init variable early
 * APOLLO_MAY_ALIAS
 * APOLLO_DISABLE_GCC_WARNING
 * APOLLO_RESTORE_GCC_WARNING
 * APOLLO_DISABLE_CLANG_WARNING
 * APOLLO_RESTORE_CLANG_WARNING
 * APOLLO_DISABLE_WARING
 * APOLLO_RESTORE_WARING
 * APOLLO_UNUSED
 * APOLLO_EMPTY
 * APOLLO_FORCE_INLINE
 * APOLLO_NO_INLINE
 * APOLLO_NON_COPYABLE
 * APOLLO_OPTIMIZE_OFF
 * APOLLO_OPTIMIZE_ON
 * APOLLO_DISABLE_DEFAULT_CTOR
 * APOLLO_DISABLE_COPY_CTOR
 * APOLLO_DISABLE_MOVE_CTOR
 * APOLLO_DISABLE_MOVE_OPERATOR
 * APOLLO_DISABLE_ASSIGNMENT_OPERATOR
 */
#ifndef APOLLO_PREPROCESSOR_JOIN
    #define APOLLO_PREPROCESSOR_JOIN(a, b) APOLLO_PREPROCESSOR_JOIN1(a, b)
    #define APOLLO_PREPROCESSOR_JOIN1(a, b) APOLLO_PREPROCESSOR_JOIN2(a, b)
    #define APOLLO_PREPROCESSOR_JOIN2(a, b) a##b
#endif

#ifndef APOLLO_STRINGIF
    #define APOLLO_STRINGIF(x) APOLLO_STRINGIF_IMPL(x)
    #define APOLLO_STRINGIF_IMPL(x) #x
#endif

#ifndef APOLLO_COMPILER_INTMAX_SIZE
    #if defined(APOLLO_COMPILER_GNUC) && defined(APOLLO_PROCESSOR_X86_64)
        #define APOLLO_COMPILER_INTMAX_SIZE 16
    #else 
        #define APOLLO_COMPILER_INTMAX_SIZE 8
    #endif
#endif

#ifndef APOLLO_OFFSETOF
    #if defined(APOLLO_COMPILER_GNUC)
        /**  
         * @note   // We can't use GCC 4's __builtin_offsetof because 
         * it mistakenly complains about non-PODs that are really PODs.
         */
        #define APOLLO_OFFSETOF(struct_, member_) ((size_t)(((uintptr_t)&reinterpret_cast<const volatile char&>((((struct_*)65536)->member_))) - 65536))
    #else
        #define APOLLO_OFFSETOF(struct_, member_) offsetof(struct_, member_)
    #endif
#endif

#ifndef APOLLO_COMPILER_NO_EXTENDED_SIZEOF
    #define APOLLO_SIZEOF_MEMBER(struct_, member_) (sizeof(struct_::member_))
#else
    #define APOLLO_SIZEOF_MEMBER(struct_, member_) (sizeof(((struct_*)0)->member_))
#endif

#ifndef APOLLO_LIKELY
	#if defined(APOLLO_COMPILER_GNUC) 
		#if defined(__cplusplus)
		    #define APOLLO_LIKELY(x)   __builtin_expect(!!(x), true)
		    #define APOLLO_UNLIKELY(x) __builtin_expect(!!(x), false) 
		#else
		    #define APOLLO_LIKELY(x)   __builtin_expect(!!(x), 1)
		    #define APOLLO_UNLIKELY(x) __builtin_expect(!!(x), 0) 
		#endif
	#else
	    #define APOLLO_LIKELY(x)   (x)
	    #define APOLLO_UNLIKELY(x) (x)
	#endif
#endif

#ifndef APOLLO_INIT_PROPERITY
    #if defined(APOLLO_COMPILER_GNUC)
        #define APOLLO_INIT_PROPERITY(x) __attribute__ ((init_priority (x)))
    #else
        #define APOLLO_INIT_PROPERITY(x)
    #endif
#endif

#ifndef APOLLO_MAY_ALIAS
    #if defined(APOLLO_COMPILER_GNUC)
        #define APOLLO_MAY_ALIAS __attribute__((__may_alias__))
    #else
        #define  APOLLO_MAY_ALIAS
    #endif
#endif

#ifndef APOLLO_DISABLE_GCC_WARNING
    #if defined(APOLLO_COMPILER_GNUC)
        #define APOLLOGCCWHELP0(x) #x
        #define APOLLOGCCWHELP1(x) APOLLOGCCWHELP0(GCC diagnostic ignored x)
        #define APOLLOGCCWHELP2(x) APOLLOGCCWHELP1(#x)
    #endif

    #if defined(APOLLO_COMPILER_GNUC) && (APOLLO_COMPILER_VERSION >= 4006) // Can't test directly for __GNUC__ because some compilers lie.
        #define APOLLO_DISABLE_GCC_WARNING(w)   \
            _Pragma("GCC diagnostic push")  \
            _Pragma(APOLLOGCCWHELP2(w))
    #elif defined(APOLLO_COMPILER_GNUC) && (APOLLO_COMPILER_VERSION >= 4004)
        #define APOLLO_DISABLE_GCC_WARNING(w)   \
            _Pragma(EAGCCWHELP2(w))
    #else
        #define APOLLO_DISABLE_GCC_WARNING(w)
    #endif
#endif

#ifndef APOLLO_RESTORE_GCC_WARNING
    #if defined(APOLLO_COMPILER_GNUC) && (APOLLO_COMPILER_VERSION >= 4006)
        #define APOLLO_RESTORE_GCC_WARNING()    \
		    _Pragma("GCC diagnostic pop")
	#else
	    #define APOLLO_RESTORE_GCC_WARNING()
	#endif
#endif

#ifndef APOLLO_DISABLE_CLANG_WARNING
    #if defined(APOLLO_COMPILER_CLANG)
        #define APOLLOCLANGWHELP0(x) #x
        #define APOLLOCLANGWHELP1(x) APOLLOCLANGWHELP0(clang diagnostic ignored x)
        #define APOLLOCLANGWHELP2(x) APOLLOCLANGWHELP1(#x)
        #define APOLLO_DISABLE_CLANG_WARNING(w)   \
            _Pragma("clang diagnostic push")  \
            _Pragma(APOLLOCLANGWHELP2(w))
	#else
        #define APOLLO_DISABLE_CLANG_WARNING(w)
    #endif
#endif

#ifndef APOLLO_RESTORE_CLANG_WARNING
    #if defined(APOLLO_COMPILER_CLANG)
		#define APOLLO_RESTORE_CLANG_WARNING()    \
			_Pragma("clang diagnostic pop")
	#else
		#define APOLLO_RESTORE_CLANG_WARNING()
	#endif
#endif

#ifndef APOLLO_DISABLE_WARING
    #if APOLLO_COMPILER_CLANG
        #define APOLLO_DISABLE_WARING(w) APOLLO_DISABLE_CLANG_WARNING(w)
    #elif APOLLO_COMPILER_GNUC
        #define APOLLO_DISABLE_WARING(w) APOLLO_DISABLE_GCC_WARNING(w)
    #endif
#endif

#ifndef APOLLO_RESTORE_WARNING
    #if APOLLO_COMPILER_CLANG
        #define APOLLO_RESTORE_WARNING() APOLLO_RESTORE_CLANG_WARNING()
    #elif APOLLO_COMPILER_GNUC
        #define APOLLO_RESTORE_WARNING() APOLLO_RESTORE_GCC_WARNING()
    #endif
#endif
#ifndef APOLLO_UNUSED
    #define APOLLO_UNUSED(x) (void)x
#endif

#ifndef APOLLO_EMPTY
    #define APOLLO_EMPTY (void)0
#endif

#ifndef APOLLO_FORCE_INLINE
    #if defined(_cplusplus)
        #define APOLLO_FORCE_INLINE inline __attribute__((always_inline))
    #else
        #define APOLLO_FORCE_INLINE __inline__ __attribute__((always_inline))
    #endif 
#endif

#ifndef APOLLO_NO_INLINE 
    #define APOLLO_NO_INLINE __attribute__((noinline))
#endif

#if !defined(APOLLO_NON_COPYABLE)
    #if defined(APOLLO_COMPILER_NO_DELETED_FUNCTIONS)
	    #define APOLLO_NON_COPYABLE(Class_)               \
		private:                                      \
			Class_(const Class_&);                  \
			void operator=(const Class_&)
	#else
		#define APOLLO_NON_COPYABLE(Class_)               \
			Class_(const Class_&) = delete;         \
			void operator=(const Class_&) = delete
     #endif
#endif

#if !defined(APOLLO_OPTIMIZE_OFF)
	#if defined(APOLLO_COMPILER_GNUC) && (EA_COMPILER_VERSION > 4004) && (defined(__i386__) || defined(__x86_64__)) // GCC 4.4+ - Seems to work only on x86/Linux so far. However, GCC 4.4 itself appears broken and screws up parameter passing conventions.
	    #define APOLLO_OPTIMIZE_OFF()            \
		    _Pragma("GCC push_options")      \
			_Pragma("GCC optimize 0")
    #elif defined(APOLLO_COMPILER_CLANG)
        #define APOLLO_OPTIMIZE_OFF() \
				APOLLO_DISABLE_CLANG_WARNING(-Wunknown-pragmas) \
				_Pragma("clang optimize off") \
				APOLLO_RESTORE_CLANG_WARNING()
	#else
		#define APOLLO_OPTIMIZE_OFF()
	#endif
#endif

#if !defined(APOLLO_OPTIMIZE_ON)
    #if defined(APOLLO_COMPILER_GNUC) && (APOLLO_COMPILER_VERSION > 4004) && (defined(__i386__) || defined(__x86_64__)) // GCC 4.4+ - Seems to work only on x86/Linux so far. However, GCC 4.4 itself appears broken and screws up parameter passing conventions.
			#define EA_OPTIMIZE_ON() _Pragma("GCC pop_options")
    #elif defined(APOLLO_COMPILER_CLANG)
        #define APOLLO_OPTIMIZE_ON() \
		    APOLLO_DISABLE_CLANG_WARNING(-Wunknown-pragmas) \
			_Pragma("clang optimize on") \
			APOLLO_RESTORE_CLANG_WARNING()
	#else
		#define APOLLO_OPTIMIZE_ON()
	#endif
#endif


#if defined(APOLLO_COMPILER_NO_DELETED_FUNCTIONS)
    #define APOLLO_FUNCTION_DELETE
#else
    #define APOLLO_FUNCTION_DELETE = delete
#endif

#ifndef APOLLO_DISABLE_DEFAULT_CTOR
    #define APOLLO_DISABLE_DEFAULT_CTOR(ClassName) ClassName() APOLLO_FUNCTION_DELETE
#endif

#ifndef APOLLO_DISABLE_COPY_CTOR
    #define APOLLO_DISABLE_COPY_CTOR(ClassName) ClassName(const ClassName &) APOLLO_FUNCTION_DELETE
#endif

#ifndef APOLLO_DISABLE_MOVE_CTOR
    #define APOLLO_DISABLE_MOVE_CTOR(ClassName) ClassName(ClassName&&) APOLLO_FUNCTION_DELETE
#endif

#ifndef APOLLO_DISABLE_ASSIGNMENT_OPERATOR
    #define APOLLO_DISABLE_ASSIGNMENT_OPERATOR(ClassName) ClassName & operator=(const ClassName &) APOLLO_FUNCTION_DELETE
#endif

#ifndef APOLLO_DISABLE_MOVE_OPERATOR
#define APOLLO_DISABLE_MOVE_OPERATOR(ClassName) ClassName & operator=(ClassName&&) APOLLO_FUNCTION_DELETE
#endif
#endif //APOLLO_CONFIG_DETAIL_COMPILER_TRAITS_H_